/*  This file is part of -_-.

    -_- is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    -_- is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with -_-.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2012-2013 Oliver Katz */

#ifndef __OPAL_CODESERVER_H
#define __OPAL_CODESERVER_H

#include "../error.h"
#include "../lexer.h"
#include "../parser.h"
#include "../macro.h"
#include "../../vm/vm.h"

#include <ctype.h>
#include <sys/wait.h>
#include <readline/readline.h>
#include <readline/history.h>

//namespace sleepyface::opal
namespace sleepyface
{
	namespace opal
	{
		//class CodeServer: a server for compiling and executing Opal code, also contains an interactive Opal shell
		class CodeServer
		{
		public:
			AST parseAST(string line, string file); //methodof CodeServer: parse a single line into AST
			AST evaluateMacros(AST ast, string file); //methodof CodeServer: evaluate the macros used in 'ast'
			vm::LiteralType compileAtomicType(AST ast, string file); //methodof CodeServer: compile a LiteralType
			vm::LiteralInt compileAtomicInt(AST ast, string file); //methodof CodeServer: compile a LiteralInt
			vm::LiteralDouble compileAtomicDouble(AST ast, string file); //methodof CodeServer: compile a LiteralDouble
			vm::LiteralAddress compileAtomicAddress(AST ast, string file); //methodof CodeServer: compile a LiteralAddress
			vm::LiteralValue compileAtomicValue(AST ast, string file); //methodof CodeServer: compile a LiteralValue
			vm::MutableDefinition compileNew(AST ast, string file); //methodof CodeServer: compile a MutableDefinition
			vm::MutableAssignment compileAssign(AST ast, string file); //methodof CodeServer: compile a MutableAssignment
			vm::Operation compileOperation(AST ast, string file); //methodof CodeServer: compile an Operation
			vm::Call compileCall(AST ast, string file); //methodof CodeServer: compile a Call
			vm::IfStatement compileIfStatement(AST ast, string file); //methodof CodeServer: compile an IfStatement
			vm::ForLoop compileForLoop(AST ast, string file); //methodof CodeServer: compile a ForLoop
			vm::FunctionalNode compileFunctionalNode(AST ast, string file); //methodof CodeServer: compile a FunctionalNode (which can be any of the above)
			vm::Prototype compilePrototype(AST ast, string file); //methodof CodeServer: compile a Prototype
			vm::Body compileBody(AST ast, string file); //methodof CodeServer: compile a Body
			pair<string, void *> compileMain(AST ast, string file, vm::VM &vm); //methodof CodeServer: compile and JIT a main function
			//returnof compileMain: pair<string, void *>: the first of the pair is the name of the function, while the second is the callback pointer

			vm::Body createWrapperFunction(string name, vm::FunctionalNode node); //methodof CodeServer: creates a wrapper function around a node (for use in the shell)
			vm::LiteralValue executeFunction(string name, void *ptr); //methodof CodeServer: executes an already-compiled function to a LiteralValue

		private:
			Lexer lexer;
			Parser parser;
			AST ast;

			map<string, string> _cfg;
			vector<string> importPath;
			Lexer importPathLexer;
			void updateImportPath();

			map<string, AST> directMacros;
			map<string, FunctionalMacro> functionalMacros;

			map<string, bool> boolCfg;
			void updateBoolCfg();
			void addBoolCfg(string k);

			map<string, vm::LiteralType> mutables;
			map<string, vm::Prototype> protos;
			map<string, vm::Body> funcs;
			map<string, AST> asts;

			bool isInteger(string s);
			bool isDouble(string s);
			bool isAddress(string s);
			bool isString(string s);
			bool isSymbol(string s);
			bool isValue(string s);
			bool isFunction(string s);
			bool isVariable(string s);

			vector<string> ops;
			bool isOperation(string s);

			vm::LiteralType proofOfType(vm::FunctionalNode n);

			void initCfg();
			void initReserved();

		public:
			CodeServer(); //methodof CodeServer: initializer with default parsing rules
			CodeServer(string whitespace, char leftBracket, char rightBracket, string leftQuotes, string rightQuotes); //methodof CodeServer: initializer with parsing rules
			~CodeServer(); //methodof CodeServer: uninitializer
			bool getDebugLLVMModule(); //methodof CodeServer: retrieves the value from internal configuration for whether the LLVM module debugging is enabled
			int macro(string name, AST body); //methodof CodeServer: defines a new direct macro
			int macro(string name, vector<string> args, AST body); //methodof CodeServer: defines a new functional macro
			AST getFunctionAST(string name);
			string cfg(string k); //methodof CodeServer: gets the internal configuration value 'k'
			string cfg(string k, string v); //methodof CodeServer: sets the internal configuration value 'k' to 'v'
			void *compile(string path, vm::VM &vm); //methodof CodeServer: compiles a file to a main function callback pointer
			void shell(vm::VM &vm); //methodof CodeServer: runs the shell in the foreground
			void shellBackground(vm::VM &vm); //methodof CodeServer: runs the shell in the background
		};
	}
}

/// For information on how to use a codeserver in C++, look at opal/codeserver/test_codeserver.cpp.

#endif